import java.util.ArrayList;
import java.util.Scanner;


public abstract class Maquina {
	private ArrayList<Dinero> dineros;
	private ArrayList<Caja> cajas;
	private int vecesSinCambio;
	
	public Maquina() {
		dineros = new ArrayList<Dinero>();
		cajas = new ArrayList<Caja>();
	}

	public void vender(){}
	
	public void agregarProducto(){}
	public void modificarProducto(){}
	public void eliminarProducto(){}
	public void listarProductos(){}
	
	public void agregarVenta(){}
	public void listarVentas(){}
	
	public void calcularVenta(){}
	
	public void informarVentas(){}
	
	public abstract void menuUsuario();
	public abstract void menuMantenimiento();
	
	public float ingresarDinero()
	{				
		float deposito = obtenerMontoADepositar();
		if (hayMonedas(deposito))
		{
			return confirmarIngresoDetallado(deposito);
		}
		else
		{
			vecesSinCambio++;
			System.out.println("No hay monedas para realizar la transaccion");
			return 0;
		}
	}
	
	public float redondeo(float cambio) 
	{	
		double redondeadoConDos = Math.round(cambio*100.0)/100.0;	
		return new Double (redondeadoConDos).floatValue();	
	}
	
	public float ingresarDinero(float precio)
	{
		float deposito = obtenerMontoADepositar();
		if (deposito < precio)
		{
			System.out.println("El importe ingresado no es suficiente para abonar este Producto.");
			return 0;
		}
		
		if (hayMonedasParaEntregar(redondeo(deposito - precio)))
		{
			if (hayMonedas(deposito))
			{	
				return confirmarIngresoDetallado(deposito);
			}
			else
			{
				System.out.println("No hay monedas para realizar la transaccion");
				return 0;
			}
		}
		else
		{
			vecesSinCambio++;
			System.out.println("No hay cambio. Ingrese importe exacto.");
			return 0;
		}
	}
	
	public float obtenerMontoADepositar()
	{
		Scanner entrada = new Scanner(System.in);
		float monto;
		
		do
		{
			System.out.println("Ingrese el monto a depositar");
			monto = entrada.nextFloat();
		}while(monto == 0);
		
		return monto;
	}
	
	public float confirmarIngresoDetallado(float deposito)
	{
		Scanner entrada = new Scanner(System.in);
		float totalAux = 0;
		byte opc = 0;
		int cantidad = 0;
		
		do 
		{
			int cont = 0;
			System.out.println("Ingrese el valor de la moneda con la que realizo el deposito:");
			for(Dinero d:dineros )
			{
				System.out.println("Opcion: " + cont + " - " + d.darValor());
				cont++;
			}
			System.out.println("Opcion: " + dineros.size() + " - Cancelar operacion");
			opc = entrada.nextByte();
			
			if (opc < dineros.size())
			{
				System.out.println("Ingrese la cantidad que deposito de la moneda seleccionada:");
				cantidad = entrada.nextInt();
				
				if(dineros.get(opc).montoPorValor(cantidad) > deposito)
				{
					System.out.println("El monto seleccionado es mayor a lo que iba a depositar, vuelva a intentar.");
					System.out.println("Tome su cambio de : " + totalAux);
					entregarCambio(totalAux);
					return 0;
				}
				
				totalAux = totalAux + dineros.get(opc).montoPorValor(cantidad);
				System.out.println("Saldo parcial: " + totalAux);
				agregarDinero(cantidad, dineros.get(opc).darValor());
			}
			else
				if (opc == dineros.size())
					entregarCambio(totalAux);
		
			cont = 0;
		}while(opc != dineros.size() && totalAux < deposito);
	
		if(totalAux == deposito)
			return totalAux;
		else //totalAux es mayor, el usuario mintio sobre lo que ingreso.
			return 0;
	}
	
	public void entregarCambio(float deposito)
	{
		float depositoRestante = deposito;
		
		int i = 0;
		
		while(i < dineros.size() && depositoRestante != 0)
		{
			while(depositoRestante >= dineros.get(i).darValor() && !dineros.get(i).sosBillete() && dineros.get(i).darCantidad() != 0)
			{
				depositoRestante = depositoRestante - dineros.get(i).darValor();
				dineros.get(i).restarCantidad();
			}
			i++;
		}
	}
	
	public boolean quieroModificar(String atributo)
	{
		Scanner entrada = new Scanner(System.in);
		
		System.out.println("Quiere modificar " + atributo + " s/n");
		String opc = entrada.next();
		
		return opc.equalsIgnoreCase("s");  
	}
	
	public void agregarDinero()
	{
		Scanner in = new Scanner(System.in);
		in.useDelimiter(System.getProperty("line.separator"));
		System.out.print("Ingrese Valor: ");
		float val = in.nextFloat();
		
		if(buscarDinero(val) != null)
		{
			System.out.print("Ya existe ese valor.");
		}
		else
		{
			int opc = 0;
			do
			{
				System.out.print("1-Billete o 2-Moneda: ");
				opc = in.nextInt();
			}while(opc != 1 && opc != 2);
			
			System.out.print("Ingrese Cantidad: ");
			int cant = in.nextInt();
			
			Dinero nuevo;
			if(opc == 1)
				nuevo = new Billete(val, cant);
			else
				nuevo = new Moneda(val, cant);
			
			if (dineros.size() == 0)
				dineros.add(nuevo);
			else
			{
				ArrayList<Dinero> dinerosAux = new ArrayList<Dinero>();
				
				int i = 0;
				
				//Paso lo existente a un array auxiliar para dejar el original ordenado
				while (i < dineros.size())
				{
					dinerosAux.add(dineros.get(i));
					i++;
				}
				
				dineros.clear();
				
				i = 0;	
				
				do
				{
					if(dinerosAux.get(i).darValor() < nuevo.darValor())
						dineros.add(nuevo);
					else
					{
						dineros.add(dinerosAux.get(i));
						i++;
					}
				}while(i < dinerosAux.size() && dineros.get(dineros.size()-1).darValor() != nuevo.darValor());
				
				if(i < dinerosAux.size() && dineros.get(i).darValor() == nuevo.darValor())
					while (i < dinerosAux.size()) //el nuevo valor se agrego al principio o en el medio, completo con lo que queda.
					{
						dineros.add(dinerosAux.get(i));	
						i++;
					}
				else //va en el ultimo.
					dineros.add(nuevo);
			}
		}
	}
	
	public void agregarDinero(int cantidad, float valor)
	{
		int i=0;
		
		while (i<dineros.size() && dineros.get(i).sos(valor) != true)
			i++;
		
		if (i < dineros.size() && dineros.get(i).sos(valor) == true)
			dineros.get(i).agregarDineroPorCompra(cantidad);
	}
	
	public void modificarDinero()
	{
		Scanner in = new Scanner(System.in);
		in.useDelimiter(System.getProperty("line.separator"));

		System.out.print("Ingrese Valor: ");
		float val = in.nextFloat();
		
		Dinero dinero = buscarDinero(val);
		
		if(dinero == null)
			System.out.print("El Valor no existe.");
		else
		{
			if (quieroModificar("Cantidad"))
			{
				System.out.print("Ingrese Cantidad: ");
				dinero.setCantidad(in.nextInt());
			}
		}
	}
	
	public void eliminarDinero()
	{
		Scanner in = new Scanner(System.in);
		in.useDelimiter(System.getProperty("line.separator"));
		
		System.out.print("Ingrese Valor: ");
		float val = in.nextFloat();
		
		int i = 0;
		
		while(i < dineros.size() && dineros.get(i).sos(val) != true)
			i++;
		
		if(i < dineros.size())
		{
			dineros.remove(dineros.get(i));
			System.out.print("Dinero eliminado.");
		}
		else
			System.out.print("El valor no existe.");
	}
	
	public Dinero buscarDinero(float valor)
	{
		int i = 0;
		
		while(i < dineros.size() && dineros.get(i).sos(valor) != true)
			i++;
		
		if(i<dineros.size())
			return dineros.get(i);
		else
			return null;
	}
	
	public void listarDineros()
	{
		Scanner in = new Scanner(System.in);
		in.useDelimiter(System.getProperty("line.separator"));
		int i = 0;
		
		while(i < dineros.size())
		{
			System.out.println(dineros.get(i).toString());
			 
			i++;
		}
	}
	
	public Caja agregarCaja(float ingreso, float egreso)
	{
		Scanner in = new Scanner(System.in);
		in.useDelimiter(System.getProperty("line.separator"));
		
		Caja caja = new Caja(ingreso, egreso);
		
		cajas.add(caja);
		
		return caja;
	}
	
	public void listarCajas()
	{
		Scanner in = new Scanner(System.in);
		in.useDelimiter(System.getProperty("line.separator"));
		int i = 0;
		
		while(i < cajas.size())
		{
			System.out.println(cajas.get(i).toString());
			 
			i++;
		}
	}
	
	public void arqueoDeCaja()
	{
		int i = 0;
		float arqueoTotal = 0;
		
		while (i<cajas.size())
		{
			arqueoTotal = arqueoTotal + cajas.get(i).ingresoMenosEgreso();	
			i++;
		}
		
		System.out.println("En un total de "+i+" transacciones por cambio realizadas, la diferencia en caja es de "+arqueoTotal);
	}
	
	public float totalMonedas()
	{
		float total = 0;
		int i = 0;
		
		while (i < dineros.size())
		{ 
			if(!dineros.get(i).sosBillete())
				total = total + dineros.get(i).montoPorValor();
		
			i++;
		}
		
		return redondeo(total);
	}
	
	public boolean hayMonedas(float deposito)
	{
		return totalMonedas() >= deposito;
	}
	
	public boolean hayMonedasParaEntregar(float cambio)
	{
		int i = 0;
		int cantidadMonedas=0;
		float depositoRestante = cambio;
		
		while(i < dineros.size() && depositoRestante != 0)
		{
			cantidadMonedas = dineros.get(i).darCantidad();
			while(depositoRestante >= dineros.get(i).darValor() && cantidadMonedas!=0)
			{
				depositoRestante = depositoRestante - dineros.get(i).darValor();
				cantidadMonedas--;
			}
			i++;
		}
		return (redondeo(depositoRestante) == 0);
	}
	
	public void informarVecesSinCambio()
	{
		System.out.println("La maquina no pudo hacer la transaccion " + vecesSinCambio + " veces por no tener cambio.");
	}
}